﻿using NUnit.Framework;
using OpenWaves.Caching;

namespace OpenWaves.UnitTests.Caching
{
    [TestFixture]
    public class MonitorBasedLockManagerTests
    {
        [Test]
        public void Lock_cannot_be_acquired_twice()
        {
            var lockManager = new MonitorBasedLockManager();
            lockManager.AcquireLock("key");

            Assert.That(lockManager.TryAcquireLock("key"), Is.False);
        }

        [Test]
        public void Acquired_lock_can_be_acquired_again_after_it_is_released()
        {
            var lockManager = new MonitorBasedLockManager();
            lockManager.AcquireLock("key");
            lockManager.ReleaseLock("key");

            Assert.That(lockManager.TryAcquireLock("key"), Is.True);
        }

        [Test]
        public void Lock_cannot_be_acquired_twice_from_two_threads()
        {
            var lockManager = new MonitorBasedLockManager();

            var threadCoordinator = new TestThreadsCoordinator();

            threadCoordinator.RunThreads(
                () =>
                    {
                        lockManager.AcquireLock("key");

                        threadCoordinator.WaitForTick(2);

                        lockManager.ReleaseLock("key");
                    },
                () =>
                    {
                        threadCoordinator.WaitForTick(1);

                        Assert.That(lockManager.TryAcquireLock("key"), Is.False);
                    });
        }

        [Test]
        public void Lock_acquired_can_be_acquired_again_in_another_thread_after_it_is_released()
        {
            var lockManager = new MonitorBasedLockManager();

            var threadCoordinator = new TestThreadsCoordinator();

            int marker = 0;

            threadCoordinator.RunThreads(
                () =>
                {
                    lockManager.AcquireLock("key");
                    marker = 1;

                    threadCoordinator.WaitForTick(2);

                    Assert.That(marker, Is.EqualTo(1));
                    lockManager.ReleaseLock("key");
                },
                () =>
                {
                    threadCoordinator.WaitForTick(1);

                    lockManager.AcquireLock("key");
                    marker = 2;

                    threadCoordinator.WaitForTick(3);

                    Assert.That(marker, Is.EqualTo(2));
                    lockManager.ReleaseLock("key");
                });            
        }
    }
}